package application

import (
	"context"
	"encoding/json"
	"fmt"
	log "github.com/sirupsen/logrus"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"time"
	"usbipproxy/domain"
	"usbipproxy/domain/dto"
	"usbipproxy/domain/service"
	"usbipproxy/domain/usbipclientsrv"
	"usbipproxy/pb"
)

type TcpMsgUsbipClientHandleServiceImpl struct {
	msgHandle      map[pb.MsgType]pbMsgHandler
	ctx            context.Context
	msgNotifySvc   service.MsgNotifyService
	usbipServerMgr usbipclientsrv.UsbipServerMgrService
	swDevice       service.SwDeviceService
	clientSvc      service.UsbIpClientCmdService
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) Handle(msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	if msg == nil {
		return nil
	}
	//msgByte, _ := common.Marshal(msg, common.TypeJson)
	if msg.GetMsgType() != pb.MsgType_TO_CLIENT_ATTACH_DATA {
		log.Debugf("TcpMsgUsbipClientHandleServiceImpl Handle msg:%+v", msg)
	} else {
		log.Debugf("TcpMsgUsbipClientHandleServiceImpl Handle msgType:%v len(data):%v",
			msg.GetMsgType(), len(msg.GetToClientAttachDataParam().GetData()))
	}
	handleFun := impl.msgHandle[msg.MsgType]
	if handleFun != nil {
		//if userCli.IsCorrectReqId(msg.GetBase().GetReqId()) {
		handleFun(impl.ctx, msg, userCli)
		//}
	}

	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) reg(msgType pb.MsgType, fun pbMsgHandler) {
	impl.msgHandle[msgType] = fun
}

func NewTcpMsgHostHandleService(msgNotifySvc service.MsgNotifyService,
	usbipServerMgr usbipclientsrv.UsbipServerMgrService,
	swDevice service.SwDeviceService, clientSvc service.UsbIpClientCmdService) TcpMsgHandleService {
	impl := &TcpMsgUsbipClientHandleServiceImpl{
		msgHandle:      make(map[pb.MsgType]pbMsgHandler),
		ctx:            context.Background(),
		msgNotifySvc:   msgNotifySvc,
		usbipServerMgr: usbipServerMgr,
		swDevice:       swDevice,
		clientSvc:      clientSvc,
	}

	impl.reg(pb.MsgType_TO_CLIENT_ATTACH, impl.handleClientAttach)
	impl.reg(pb.MsgType_TO_CLIENT_ATTACH_DATA, impl.handleToClientAttachData)
	impl.reg(pb.MsgType_TO_CLIENT_DETACH, impl.handleToClientDetach)
	impl.reg(pb.MsgType_TO_CLIENT_ATTACH_OVER, impl.handleClientAttachOver)
	impl.reg(pb.MsgType_HEARTBEAT, impl.handleClientHeartNotify)
	impl.reg(pb.MsgType_TO_CLIENT_INFO, impl.handleClientInfo)

	return impl
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) handleClientInfo(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	clientInfoParam := msg.GetToClientInfoParam()
	reqId := msg.GetBase().GetReqId()
	log.Infof("TcpMsgUsbIpServerHandleServiceImpl handleClientInfo reqId:%s clientInfoParam:%+v begin", reqId, clientInfoParam)
	/*
		heartMsg := &pb.ProxyMsg{
			MsgType: pb.MsgType_SERVER_HEARTBEAT,
			Base:    &pb.BaseParam{},
			Param:   &pb.ProxyMsg_ServerHeartbeatParam{ServerHeartbeatParam: &pb.ServerHeartbeatParam{BusId: busId, SessId: sessId}},
		}
		impl.msgNotifySvc.SendMsg(heartMsg)
	*/
	//impl.usbipServerMgr.RecvHeartbeat(busId, sessId)
	clientInfoRsp := &dto.ClientInfoRsp{
		Code: 0,
		Msg:  "",
		Data: map[string]any{
			"hello": "hello",
		},
	}
	rspByte, _ := json.Marshal(clientInfoRsp)
	rsp := &pb.ProxyMsg{
		MsgType: pb.MsgType_RESPONSE,
		Base:    msg.GetBase(),
		Param: &pb.ProxyMsg_ResponseParam{ResponseParam: &pb.ResponseParam{
			Flag: pb.ResponseParamFlag_FLAG_END,
			Data: rspByte,
		}},
	}
	impl.msgNotifySvc.SendMsg(rsp)
	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) handleClientHeartNotify(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	busId := msg.GetHeartbeatParam().GetBusId()
	sessId := msg.GetHeartbeatParam().GetSessId()
	vendorId := msg.GetHeartbeatParam().GetVendorId()
	log.Infof("TcpMsgUsbIpServerHandleServiceImpl handleClientHeartNotify sessId:%v busId:%v vendorId:%v begin", sessId, busId, vendorId)
	/*
		heartMsg := &pb.ProxyMsg{
			MsgType: pb.MsgType_SERVER_HEARTBEAT,
			Base:    &pb.BaseParam{},
			Param:   &pb.ProxyMsg_ServerHeartbeatParam{ServerHeartbeatParam: &pb.ServerHeartbeatParam{BusId: busId, SessId: sessId}},
		}
		impl.msgNotifySvc.SendMsg(heartMsg)
	*/
	impl.usbipServerMgr.RecvHeartbeat(busId, sessId)
	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) handleClientAttach(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	//如果正在进行中的，则停止
	log.Infof("TcpMsgUsbipClientHandleServiceImpl handleClientAttach msg:%+v", msg)
	busId := msg.GetToClientAttachParam().GetBusId()
	vendorId := msg.GetToClientAttachParam().GetVendorId()
	impl.usbipServerMgr.Remove(busId)
	go func(busId string, notifyConnId int64) {
		//reqId := msg.GetClientAttachParam().GetReqId()
		usbipServer := usbipclientsrv.NewUsbipClientServerServiceImpl(busId, vendorId, notifyConnId, impl.msgNotifySvc, impl.clientSvc)
		port, err := usbipServer.Start()
		if err != nil {
			log.Errorf("handleClientAttach start tcp server err: %v", err)
			return
		}
		//defer usbipServer.Stop()
		impl.usbipServerMgr.Add(busId, usbipServer)

		log.Infof("handleClientAttach busid:%v  port:%v notifyConnId:%v", busId, port, notifyConnId)
		cmds := []string{"usbip", "--tcp-port", strconv.Itoa(port), "attach", "-r", "127.0.0.1", "-b", busId}
		log.Infof("handleClientAttach cmds:%+v usbip_instance_id:%v", cmds, userCli.GetConnId())
		cmd := exec.Command(cmds[0], cmds[1:]...)
		cmd.Env = append(os.Environ(), fmt.Sprintf("usbip_instance_id=%v", userCli.GetConnId()))
		str, err := cmd.CombinedOutput()
		log.Errorf("handleClientAttach CombinedOutput:%v", string(str))
		if err != nil {
			log.Errorf("handleClientAttach err:%v", err)
			return
		}

		log.Infof("handleClientAttach ret:%v", string(str))

		if strings.Contains(string(str), "succesfully") {

			beforeAudioDeviceList, err := impl.clientSvc.GetAudioDeviceList()
			beforeAudioDeviceListByte, _ := json.Marshal(beforeAudioDeviceList)
			log.Infof("handleClientAttach beforeAudioDeviceList:%v err:%v", string(beforeAudioDeviceListByte), err)
			//vidpids := strings.Split(vendorId, ":")
			//if len(vidpids) == 2 {
			time.AfterFunc(time.Second*3, func() {
				impl.clientSvc.AttachByParent(userCli.GetConnId())
				afterAudioDeviceList, err := impl.clientSvc.GetAudioDeviceList()
				afterAudioDeviceListByte, _ := json.Marshal(afterAudioDeviceList)
				log.Infof("handleClientAttach afterAudioDeviceListByte:%v err:%v", string(afterAudioDeviceListByte), err)

				for _, aItem := range afterAudioDeviceList {

					if strings.Contains(strings.ToLower(aItem.Name), "pure") {
						continue
					}

					find := false
					for _, bItem := range beforeAudioDeviceList {
						if aItem.ID == bItem.ID {
							find = true
							break
						}
					}

					if !find {
						log.Infof("handleClientAttach can not find device:%+v", aItem)
						err := impl.clientSvc.SetDefaultAudioDevice(aItem.Index, aItem.Type, false)
						if err != nil {
							impl.clientSvc.SetDefaultAudioDevice(aItem.Index, aItem.Type, true)
						}
						break
					}
				}
			})
			//} else {
			//	log.Infof("handleClientAttach vendorId:%v", vendorId)
			//}
		}
	}(busId, msg.GetBase().GetToId())

	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) handleClientAttachOver(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	//如果正在进行中的，则停止
	//impl.usbipServerMgr.DeleteByNotifyConnId(msg.GetBase().GetToId())
	log.Infof("handleClientAttachOver toId:%v", msg.GetBase().GetToId())

	impl.usbipServerMgr.DeleteByNotifyConnId(msg.GetBase().GetToId())
	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) handleToClientAttachData(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	busId := msg.GetToClientAttachDataParam().GetBusId()
	sessId := msg.GetToClientAttachDataParam().GetSessId()
	data := msg.GetToClientAttachDataParam().GetData()
	log.Debugf("handleToClientAttachData busId:%v  sessId:%v  len(data):%v ", busId, sessId, len(data))
	impl.usbipServerMgr.RecvAttachData(busId, sessId, data)
	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) handleToClientDetach(ctx context.Context, msg *pb.ProxyMsg, userCli *domain.DispatchClient) error {
	sessId := msg.GetToClientDetachParam().GetSessId()
	busId := msg.GetToClientDetachParam().GetBusId()
	vendorId := msg.GetToClientDetachParam().GetVendorId()
	log.Infof("handleToClientDetach toId:%v busId:%v", msg.GetBase().GetToId(), busId)

	impl.clientSvc.DetachByBusId(busId, vendorId)
	impl.usbipServerMgr.Remove(busId)

	toServerMsg := &pb.ProxyMsg{
		Base:    msg.GetBase(),
		MsgType: pb.MsgType_TO_SERVER_DETACH,
		Param: &pb.ProxyMsg_ToServerDetachParam{ToServerDetachParam: &pb.ToServerDetachParam{
			SessId:   sessId,
			BusId:    busId,
			VendorId: vendorId,
		}},
	}

	impl.msgNotifySvc.SendMsg(toServerMsg)
	return nil
}

func (impl *TcpMsgUsbipClientHandleServiceImpl) Stop() {
	impl.usbipServerMgr.Stop()
}
